<template>
  <div id="drag"></div>
</template>

<script setup>
import * as d3 from 'd3'
import { onMounted } from 'vue'
import graph from './json.js'

const { nodes, links } = graph

onMounted(() => {
  dragDemo()
})

function dragDemo() {
  const w = 660, h = 400
  let checkedId = null, hoverId = null

  const simulation = d3.forceSimulation(nodes)
    .force('charge', d3.forceManyBody().distanceMax(900).strength(-1000))
    .force('collide', d3.forceCollide().radius(() => 30).iterations(1))
    .force('link', d3.forceLink(links).id(d => d.id).distance(160))
    .force('x', d3.forceX(w / 2))
    .force('y', d3.forceY(h / 2))

  let svg = d3.select('#drag').append('svg')
    .attr('class', 'svg')
    .attr('width', w)
    .attr('height', h)
    .attr('viewBox', [0, 0, w, h])

  //未选中滤镜
  svg.append('des').append('filter').attr('id', 'no-shadow')
    .append('feOffset')
    .attr('dx', 0).attr('dy', 0)

  // 选中滤镜
  const type = ['offsetBlur', 'SourceGraphic']
  let selectedShadow = d3.select('des').append('filter').attr('id', 'selectedShadow')
    .attr('x', -10).attr('y', -10).attr('width', 30).attr('height', 30)
  selectedShadow.append('feGaussianBlur')
    .attr('in', 'SourceAlpha').attr('stdDeviation', 8).attr('result', 'blur')
  selectedShadow.append('feOffset')
    .attr('in', 'blur').attr('dx', 0).attr('dy', 0).attr('result', 'offsetBlur')
  selectedShadow.append('feMerge')
    .selectAll('feMergeNode').data(type).join('feMergeNode').attr('in', d => d)


  // hover滤镜  
  let hoverShadow = d3.select('des').append('filter').attr('id', 'hoverShadow')
    .attr('x', -10).attr('y', -10).attr('width', 60).attr('height', 60)
  hoverShadow.append('feGaussianBlur')
    .attr('in', 'SourceAlpha').attr('stdDeviation', '10').attr('result', 'blur')
  hoverShadow.append('feOffset')
    .attr('in', 'blur').attr('dx', 0).attr('dy', 0).attr('result', 'offsetBlur')
  hoverShadow.append('feMerge')
    .selectAll('feMergeNode').data(type).join('feMergeNode').attr('in', d => d)



  let line = svg.append('g')
    .selectAll('line')
    .data(links)
    .join('path')
    .attr('stroke', 'rgba(0,0,0,0.05)')
    .attr('stroke-width', 1.5)


  let node = svg.append('g')
    .selectAll("g")
    .data(nodes)
    .join('g')
    .call(drag(simulation))
    .classed("node", true)

  node.append('circle')
    .attr('r', d => {
      const size = [36, 30, 25, 22]
      return size[d.category] || 22
    })
    .attr('fill', d => {
      let color = ''
      switch (d.category) {
        case 0: color = 'rgba(0,68,69,0.6)'; break
        case 1: color = 'rgba(0,68,69,0.4)'; break
        case 2: color = 'rgba(0,68,69,0.3)'; break
        default: color = 'rgba(0,68,69,0.2)'; break
      }
      return color
    })
    .attr('stroke', 'rgba(255,255,255,0.5)')
    .attr('stroke-width', 1)

  node.append('circle')
    .attr('class', 'nodeBorder')
    .attr('r', d => {
      const size = [38, 32, 27, 24]
      return size[d.category] || 24
    })
    .attr('fill', 'none')
    .attr('stroke', 'rgba(255,255,255,0.8)')
    .attr('stroke-width', 3)
    .attr('id', d => 'bg_' + d.id)
  // .attr('filter', 'url(#hoverShadow)')
  // .attr('filter', 'url(#selectedShadow)')


  node.append('text')
    .text(d => d.name)
    .attr('text-anchor', 'middle')
    .attr('stroke', 'rgba(255, 255, 255, 0.6)')
    .attr('stroke-width', 1)
    .attr('font-size', '12px')

  // 节点鼠标经过效果
  node.on('mouseover', e => {
    hoverId = e.target.__data__.id
    console.log(hoverId, checkedId, 7777)
    if (checkedId === hoverId) { return }
    svg.selectAll('.nodeBorder').filter(d => { d.id !== checkedId }).attr('filter', '')
    svg.select(`#bg_${hoverId}`).attr('filter', 'url(#hoverShadow)')
  }).on('mouseout', e => {
    if (checkedId !== hoverId) {
      d3.select(`#bg_${hoverId}`).attr('filter', '')
    }
    hoverId = ''
  })
  // 节点鼠标点击效果  
  node.on('click', e => {
    checkedId = e.target.__data__.id
    d3.selectAll('.nodeBorder').attr('filter', '')
    d3.select(`#bg_${checkedId}`)
      .attr('filter', 'url(#selectedShadow)')
  })





  simulation.on('tick', () => {
    line.attr('d', d => {
      if (d.source.x < d.target.x) {
        return `M ${d.source.x} ${d.source.y} L ${d.target.x} ${d.target.y}`
      } else {
        return `M ${d.target.x} ${d.target.y} L ${d.source.x} ${d.source.y}`
      }
    })
    node.attr("transform", d => `translate(${d.x},${d.y})`);
  })
}

const drag = (simulation) => {
  function dragstarted(event, d) {
    if (!event.active) simulation.alphaTarget(0.3).restart();
    d.fx = d.x;
    d.fy = d.y;
  }

  function dragged(event, d) {
    d.fx = event.x;
    d.fy = event.y;
  }

  function dragended(event, d) {
    if (!event.active) simulation.alphaTarget(0);
    d.fx = null;
    d.fy = null;
  }
  return d3.drag()
    .on("start", dragstarted)
    .on("drag", dragged)
    .on("end", dragended);
}

</script>
<style lang="less">
.node {
  cursor: move;
}
</style>